using System;
using System.Collections;
using System.Data;
using BO = PHSRAG.Insight21.BusinessObjects;
using Utility = PHSRAG.Utility;
using ICachePolicy = PHSRAG.Insight21.Policy.ICachePolicy;
using DataAccess = PHSRAG.Utility.DataAccess;
using System.Data.SqlClient;
using FormsManagement = PHSRAG.Insight21.FormsManagement;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;



namespace PHSRAG.Insight21.Humans
{
    /// <summary>
    /// Represents an amendment to a protocol.
    /// </summary>
    public class Amendment : ProtocolProcess
    {
        /// <summary>
        /// Initializes the base with the specificed cache policy and sets the type of the
        /// process to Amendment
        /// </summary>
        /// <param name="cachePolicy">the policy used to cache the Protocol object</param>
        public Amendment(ICachePolicy cachePolicy)
            : base(cachePolicy)
        {
            // set the type.
            Type = Humans.Constants.ProtocolProcessType.Amendment;
        }

        #region Constructor
        /// <summary>
        /// Constructor for Amendment which takes a data row.
        /// </summary>
        /// <param name="cachePolicy">the policy used to cache the Protocol object</param>
        /// <param name="dataRow"></param>
        public Amendment(ICachePolicy cachePolicy, DataRow dataRow)
            : base(cachePolicy, dataRow)
        {
            // set the type.
            Type = Humans.Constants.ProtocolProcessType.Amendment;
        }
        #endregion

        #region Overridden Methods
        public override void Approve(SqlTransaction insightTransaction, SqlTransaction rexTransaction,
                string insightConnectionString, string rexConnectionString, Protocol protocol)
        {
            base.Approve(insightTransaction, rexTransaction, insightConnectionString, rexConnectionString, protocol);

            Humans.RexParams rexParameters = RunApprovalScript();

            if (rexParameters.NewProtocolTitle != string.Empty || rexParameters.NewProtocolVersion != string.Empty)
            {
                if (rexParameters.NewProtocolTitle != string.Empty)
                    protocol.Title = rexParameters.NewProtocolTitle;

                if (rexParameters.NewProtocolVersion != string.Empty)
                    protocol.Version = rexParameters.NewProtocolVersion;
            }
            if (this.Protocol.ModuleType == Constants.ModuleType.Animals)
            {
                if (((AnimalsRexParams)rexParameters).TotalNumberOfAnimals != 0)
                {
                    protocol.NumberOfAnimals = ((AnimalsRexParams)rexParameters).TotalNumberOfAnimals;
                }
            }

            /// Here we need to set the appropriate IsCurrent flag on the 
            /// People and Documents, and for any People (Staff) that 
            /// have been removed, call Delete on each of them to 
            /// remove their contact info (if any) and their direct access
            /// to this protocol.
            /// 

            /// ALSO!!!  If the PI changed, make sure to 
            /// set the PIPersonId and the Organization ID of the Protocol here...
            /// 

            /// FINALLY, get a new Version of the Initial Review process for editing,
            /// go through and copy / overwrite / delete the QuestionnaireAnswers on the 
            /// initial review with the ones from the Amendment, since the amendment has been approved
            /// and takes precedence.
			ProtocolVersion latestIRVersion = Protocol.GetLatestApplicationVersion(
                        ProtocolId,
                        insightConnectionString, false,
                        CachePolicy);

            ProtocolProcess IR = null;

            // preload these because we run into DB Blocking issues with the way this is done
            // if we call this later on in the method. 
            ArrayList staff = this.StudyStaff;
            ArrayList protocolStaff = protocol.StudyStaffList;

            if (latestIRVersion != null)
            {

                ProtocolVersion amendmentVersion = this.GetLatestProtocolVersion(
                    insightConnectionString,
                    false,
                    CachePolicy);

                // create a new protocolversion on the IR, copy over all QA's from the Amendment,
                // then loop through those on the Latest IR Version, and if the QuestionnaireID 
                // doesn't already exist in the new collection, then copy that QA over to the new version
                ArrayList processes = protocol.ProtocolProcesses;

                foreach (ProtocolProcess currProcess in processes)
                {
                    if (currProcess.Type == Humans.Constants.ProtocolProcessType.InitialReview ||
                            currProcess.Type == Humans.Constants.AnimalProtocolProcessType.InitialReview)
                    {
                        IR = currProcess;
                        break;
                    }

                    if (IR == null)
                    {
                        IR = ProtocolProcess.GetProtocolProcess(
                            insightConnectionString,
                            latestIRVersion.ProtocolProcessId,
                            CachePolicy,
                            null);
                    }
                }

                //ProtocolVersion newVersion = IR.AddNewVersion( CachePolicy );
                ProtocolVersion newVersion = IR.GetLatestProtocolVersion(false, CachePolicy).Clone();
                newVersion.QuestionnaireAnswers.RemoveRange(0, newVersion.QuestionnaireAnswers.Count);

                if (IR != null)
                {
                    IR.ProtocolVersions.Add(newVersion);

                    // merge the staff on teh IR.
                    foreach (StaffPerson origStaff in IR.StudyStaff)
                    {
                        if (origStaff.RemovedProtocolProcessId == this.Id)
                        {
                            //origStaff.IsCurrent = false;
                            //origStaff.Save(insightTransaction);

                            //delete the record completely. if removedppid is not set to 0 before calling delete
                            //then protocolstaff_map record is not deleted
                            //and when the approve method calls submit on the latest IR, the study staff list loaded will also contain
                            //the staff deleted by the amendment and when this latest IR is saved, the deleted staff is again given access
                            //(record inserted into FwkResource_DomainUser_Map) with permission level set to 0
                            origStaff.RemovedProtocolProcessId = 0;
                            origStaff.Delete(insightTransaction);
                        }
                    }

                    // NULL the collection, now that it's been saved
                    // since we'll reuse this collection when updating Rex- it won't know
                    // that we marked this one current. Rather than check the Current flag 
                    // when updating Rex, it's better to reload the collection altogether.
                    IR.StudyStaff = null;
                }

                /// @@@ cjr20 6/5/2009 There is a bug here.  It looks like we're trying to not add
                /// the amendment form, but we actually are, because the type is always either
                /// not equal to amendment or not equal to animals amendment, it can never equal
                /// both at the same time.  Still, we now need this form in the new IR and we're
                /// in code freeze, so I'm not changing it.  Note this code is copied exactly to
                /// Continuing Review.
                foreach (FormsManagement.QuestionnaireAnswer amendQA in amendmentVersion.QuestionnaireAnswers)
                {
                    if (amendQA.Action != (int)FormsManagement.QuestionnaireAnswer.UserAction.Remove
                            && (amendQA.QuestionnaireVersion.Questionnaire.Type != FormsManagement.Constants.QuestionnaireType.Amendment
                            || amendQA.QuestionnaireVersion.Questionnaire.Type != "QUESTCAT_ANM_AM"))
                        newVersion.QuestionnaireAnswers.Add(amendQA.Clone(false));
                }

				ArrayList toBeAddedQAs = new ArrayList();
				foreach (FormsManagement.QuestionnaireAnswer initialRevQA in latestIRVersion.QuestionnaireAnswers)
				{
					int currQuestionnaireId = initialRevQA.QuestionnaireVersion.QuestionnaireID;
					bool foundQA = false;
					foreach (FormsManagement.QuestionnaireAnswer newCopiedQA in newVersion.QuestionnaireAnswers)
					{
						if (currQuestionnaireId == newCopiedQA.QuestionnaireVersion.QuestionnaireID)
						{
							foundQA = true;
							break;
						}
					}

					if (!foundQA)
					{
						toBeAddedQAs.Add(initialRevQA.Clone(false));
					}
				}

                latestIRVersion.Status = Humans.Constants.ProtocolProcessStatus.ApprovedInitialApp;

                newVersion.Status = Humans.Constants.ProtocolProcessStatus.Merged;
            }
            else
            {
                /*create an animals IR(along with the Amendment) when the user submits an amendment on the legacy protocol. 
                The protocol will no longer be legacy after creating the IR in insight, as we are creating an initial review unlike humans module
                 * This step will enable creation of an IR process for each legacy protocol.
                */
                if (this.Type == Humans.Constants.AnimalProtocolProcessType.Amendment)
                {
                    ProtocolProcess newIRProcess = new ProtocolProcess(CachePolicy);
                    newIRProcess.Type = Constants.AnimalProtocolProcessType.InitialReview;
                    newIRProcess.Name = Constants.AnimalProtocolProcessShortType.InitialReview;
                    newIRProcess.ProtocolId = this.ProtocolId;
                    newIRProcess.SubmitterId = this.SubmitterId;
                    newIRProcess.SubmittedDate = this.SubmittedDate;
                    newIRProcess.LastModifiedByUserId = this.LastModifiedByUserId;
                    newIRProcess.LastModifiedDate = this.LastModifiedDate;
                    newIRProcess.Locked = this.Locked;
                    newIRProcess.LastLockedVersion = 1;
                    newIRProcess.Status = this.Status;
                    newIRProcess.Save(insightConnectionString);

                    latestIRVersion = this.GetLatestProtocolVersion(false, CachePolicy).Clone();
                    latestIRVersion.Version = 1;
                    latestIRVersion.Status = Humans.Constants.ProtocolProcessStatus.ApprovedInitialApp;
                    latestIRVersion.LockDate = latestIRVersion.CreateDate;
                    latestIRVersion.ProtocolProcessId = newIRProcess.Id;
                    newIRProcess.ProtocolVersions.Add(latestIRVersion);                    
                    
                }               
            }

            // merge the staff
			ArrayList removedStaffList = new ArrayList();
            foreach (StaffPerson amendStaff in staff)
            {
                if (amendStaff.AddedProtocolProcessId == Id)
                {
                    amendStaff.IsCurrent = true;

                    // update the PI if it has changed.
                    if (amendStaff.RoleId == Humans.Constants.StaffRoleType.PrincipalInvestigator)
                    {
                        protocol.PIPersonId = amendStaff.AdmPersonId;
                    }
                }
				if (amendStaff.RemovedProtocolProcessId == Id)
				{
					//amendStaff.IsCurrent = false;
					amendStaff.RemovedProtocolProcessId = 0;
					amendStaff.Delete(insightTransaction);
					removedStaffList.Add(amendStaff);
				}
            }
			//remove the deleted staff from collection
			foreach (StaffPerson removedStaff in removedStaffList)
				staff.Remove(removedStaff);

			removedStaffList = new ArrayList();
            foreach (StaffPerson origStaff in protocolStaff)
            {
                if (origStaff.RemovedProtocolProcessId == this.Id)
                {
                    //origStaff.IsCurrent = false;
                    //origStaff.Save(insightTransaction);
					origStaff.RemovedProtocolProcessId = 0;
					origStaff.Delete(insightTransaction);
					removedStaffList.Add(origStaff);
                }
            }
			//remove the deleted staff from collection
			foreach (StaffPerson removedStaff in removedStaffList)
				protocolStaff.Remove(removedStaff);

            // Preload this PP's Versions.
            int pvCount = this.ProtocolVersions.Count;

            ArrayList protVersions = this.ProtocolVersions;
            foreach (ProtocolVersion version in protVersions)
            {
                int questAnsCount = version.QuestionnaireAnswers.Count;
                foreach (FormsManagement.QuestionnaireAnswer currQA in version.QuestionnaireAnswers)
                {
                    int ansCount = currQA.Answers.Count;
                }
            }
            if (latestIRVersion != null)
                latestIRVersion.Save(insightTransaction);

            
            protocol.Save(insightTransaction);
            Save(insightTransaction);
        }

        /// <summary>
        /// This overridden method sets Added Protocol Process ID, Protocol ID and sets current flag to true.
        /// StudyStaff object gets saved after changes.		
        /// </summary>
        /// <param name="StaffPerson">StaffPerson Object</param>
        public override void AddStudyStaff(StaffPerson staffPerson)
        {
            try
            {
                staffPerson.AddedProtocolProcessId = Id;
                staffPerson.ProtocolId = ProtocolId;
                staffPerson.IsCurrent = false;
                StudyStaff.Add(staffPerson);
            }
            catch (Exception ex)
            {
                Utility.ExceptionReport.WriteToEventLog(
                    "Failed to add study staff in Amendment", ex, true, Utility.ExceptionReport.ReportType.Error);
                throw new Exception("Failed to add study staff in Amendment", ex);
            }

        }

        /// <summary>
        /// This overridden method sets Removed Protocol Process ID, and sets current flag to false.
        /// StudyStaff object gets saved after changes.
        /// </summary>
        /// <param name="connectionString">connection to the DB.</param>
        /// <param name="StaffPerson">StaffPerson Object</param>
        public override void RemoveStudyStaff(string connectionString, StaffPerson staffPerson)
        {
            try
            {
                staffPerson.RemovedProtocolProcessId = Id;
                staffPerson.Save(connectionString);
                StudyStaff.Remove(staffPerson);
            }
            catch (Exception ex)
            {
                Utility.ExceptionReport.WriteToEventLog(
                    "Failed to remove study staff in Amendment", ex, true, Utility.ExceptionReport.ReportType.Error);
                throw new Exception("Failed to remove study staff in Amendment", ex);
            }
        }

        /// <summary>
        /// For the application, if it's the first time, move over the Protocol as 
        /// well as the Initial Review, and set the Protocol ID.
        /// </summary>
        /// <param name="cachePolicy">passed in relevant cachepolicy</param>
        /// <param name="insightConnectionString">Insight DB connectionstring</param>
        /// <param name="rexConnectionString">Rex DB's connectionString</param>
        public override void Submit(ICachePolicy cachePolicy, string insightConnectionString,
                        string rexConnectionString, string executingAssemblyPath, RexParams rexParameters, bool IsRelocked)
        {
            base.Submit(cachePolicy, insightConnectionString, rexConnectionString, executingAssemblyPath, rexParameters, IsRelocked);

            Protocol currProtocol = new Protocol(cachePolicy);
            currProtocol.Load(insightConnectionString, this.ProtocolId);

            if (currProtocol.ModuleType == Constants.ModuleType.Animals)
            {
                // if the ID isn't zero, look it up to see if
                // what we have is the current attachmentTitle # (in case it's
                // been cloned in Rex bc of Amendment or Correction)
                if (currProtocol.RexProtocolId != 0)
                {
                    currProtocol.RexProtocolId = GetCurrentRexProtocolId(rexConnectionString, null, currProtocol.ProtocolNumber, Constants.ModuleType.Animals);
                    currProtocol.Save(insightConnectionString);
                }

                int rexProtId = currProtocol.RexProtocolId;
                SqlConnection rexConn = new SqlConnection(rexConnectionString);
                rexConn.Open();
                SqlTransaction rexTrans = rexConn.BeginTransaction();

                SqlConnection insightConn = new SqlConnection(insightConnectionString);
                insightConn.Open();
                SqlTransaction insightTrans = insightConn.BeginTransaction();

                try
                {
                    // call the new updated SP here...
                    DataSet results = GetLatestAnimalsRexProcessData(currProtocol.ProtocolNumber, rexTrans);

                    DateTime dateReceived = DateTime.Now;

                    if (results.Tables[0].Rows.Count > 0)
                    {

                        object o = results.Tables[0].Rows[0]["dtReceived"];
                        dateReceived = (o == DBNull.Value) ? DateTime.MinValue : (DateTime)o;

                        o = results.Tables[0].Rows[0]["nStatusId"];
                        if ((int)o != 0)
                            this.RexIRBStatusId = (int)o;

                        o = results.Tables[0].Rows[0]["nAnimalProtId"];
                        if ((int)o > 0 && (int)o != rexProtId)
                            currProtocol.RexProtocolId = (int)o;
                    }

                    // now use the RexParams here to add any Components to be Changed.for
                    Object rexIRbStatusId = Utility.DataAccess.ExecuteScalar(rexConn, rexTrans,
                                     "spAnimalAmendmentUpdate",
                                     Utility.DataAccess.BuildSqlParameterArray(
                                     "@nAnimalProtId", SqlDbType.Int, currProtocol.RexProtocolId,
                                     "@nAmendmentId", SqlDbType.Int, this.RexIRBStatusId,
                                     "@dtReceived", SqlDbType.DateTime, dateReceived, // Need to pass 00:00:00 for the time to not display in Rex
                                     "@bCurrentStatus", SqlDbType.Bit, true,
                                     "@nProtocolProcessId", SqlDbType.Int, this.Id));

                    if (DBNull.Value != rexIRbStatusId)
                        this.RexIRBStatusId = Convert.ToInt32(rexIRbStatusId);

                    // remove all the existing components to change, before re-inserting them.
                    // pass in 0 for the 2nd parameter, since that's what Rex does, per Irina.
                    int result = Convert.ToInt32(Utility.DataAccess.ExecuteScalar(rexConn, rexTrans,
                         "spAnimalAmendComponentDelete",
                         Utility.DataAccess.BuildSqlParameterArray(
                         "@nAnimalProtID", SqlDbType.Int, currProtocol.RexProtocolId,
                         "@nAmendmentID", SqlDbType.Int, this.RexIRBStatusId)));

                    foreach (RexParams.ComponentToChange component in rexParameters.ComponentsToChange)
                    {
                        int irbStatusId = Convert.ToInt32(Utility.DataAccess.ExecuteScalar(rexConn, rexTrans,
                         "spAnimalAmendComponentUpdate",
                         Utility.DataAccess.BuildSqlParameterArray(
                         "@nAnimalProtId", SqlDbType.Int, currProtocol.RexProtocolId,
                         "@nAmendmentId", SqlDbType.Int, this.RexIRBStatusId,
                         "@nAmendComponent", SqlDbType.Int, DBNull.Value,
                         "@sComponentChanged", SqlDbType.VarChar, component.Component)));
                    }

                    this.ReceivedDate = DateTime.Now;
                    this.Save(insightTrans);

                    rexTrans.Commit();
                    insightTrans.Commit();
                }
                catch
                {
                    rexTrans.Rollback();
                    throw;
                }
                finally
                {
                    if (rexConn.State != ConnectionState.Closed)
                        rexConn.Close();
                }
            }
            else
            {
                // if the ID isn't zero, look it up to see if
                // what we have is the current attachmentTitle # (in case it's
                // been cloned in Rex bc of Amendment or Correction)
                if (currProtocol.RexProtocolId != 0)
                {
                    currProtocol.RexProtocolId = base.GetCurrentRexProtocolId(rexConnectionString,null,currProtocol.ProtocolNumber,Constants.ModuleType.Humans);
                    currProtocol.Save(insightConnectionString);
                }

                int rexProtId = currProtocol.RexProtocolId;
                SqlConnection rexConn = new SqlConnection(rexConnectionString);
                rexConn.Open();
                SqlTransaction rexTrans = rexConn.BeginTransaction();

                try
                {
                    // remove all the existing components to change, before re-inserting them.
                    // pass in 0 for the 2nd parameter, since that's what Rex does, per Irina.
                    int result = Convert.ToInt32(Utility.DataAccess.ExecuteScalar(rexConn, rexTrans,
						 "spIRBStatusAmendDeleteComponents",
                         Utility.DataAccess.BuildSqlParameterArray(
                         "@nIRBProtocolID", SqlDbType.Int, currProtocol.RexProtocolId,
                         "@nAmendStatusID", SqlDbType.Int, this.RexIRBStatusId)));

                    // now use the RexParams here to add any Components to be Changed.for 
                    foreach (RexParams.ComponentToChange component in rexParameters.ComponentsToChange)
                    {
                        int irbStatusId = Convert.ToInt32(Utility.DataAccess.ExecuteScalar(rexConn, rexTrans,
                         "spIRBStatusAmendChngUpdate",
                         Utility.DataAccess.BuildSqlParameterArray(
                         "@nIRBProtocolID", SqlDbType.Int, currProtocol.RexProtocolId,
                         "@nStatusID", SqlDbType.Int, this.RexIRBStatusId,
                         "@sComponentToChange", SqlDbType.VarChar, component.Component)));
                    }

                    rexTrans.Commit();
                }
                catch
                {
                    rexTrans.Rollback();
                    throw;
                }
                finally
                {
                    if (rexConn.State != ConnectionState.Closed)
                        rexConn.Close();
                }
            }
        }

        /// <summary>
        /// This method returns the required study staff to be displayed
        /// in the View Application (currently a PDF) which is none.
        /// </summary>
        /// <returns>ArrayList containing StudyStaff</returns>
        public override ArrayList GetStudyStaffForViewApp()
        {
            if (this.Type == Constants.AnimalProtocolProcessType.Amendment)
            {
                return base.GetStudyStaffForViewApp();
            }
            return null;
        }

        #endregion
    }
}
